/*
 * Copyright (c) 2022 Hong Jiahua
 * https://gitee.com/arrco/jh_share_deque
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
/**
  * @file        jh_share_deque.c
  * @brief       共享双端队列
  * @details     共享双端队列
  * @author      Hong Jiahua
  * @version     1.0.1
  * @date        2022-02-19
  * @copyright   2022 Hong Jiahua MIT License
  */
#include "jh_share_deque.h"

/************************************************************************/
/*                                                                      */
/************************************************************************/
static int jh_share_deque_push(jh_share_deque_t* share_deque, void* item, jh_cursor_type target) {
    jh_cursor_type cursor = JH_SD_GET(share_deque, JH_SD_IDLE_HEAD, next);
    
    if(cursor) {
        JH_SD_GET(share_deque, JH_SD_IDLE_HEAD, next) = JH_SD_GET(share_deque, cursor, next);
        
        memcpy((JH_SD_ELEMENT_ADDR(share_deque, cursor)), item, share_deque->item_size);
        
        JH_SD_GET(share_deque, cursor, next) = JH_SD_GET(share_deque, target, next);
        JH_SD_GET(share_deque, cursor, prev) = target;
        
        JH_SD_GET(share_deque, JH_SD_GET(share_deque, target, next), prev) = cursor;
        JH_SD_GET(share_deque, target, next) = cursor;
        
        return 0;
    }
    
    return -1;
}

static int jh_share_deque_pop(jh_share_deque_t* share_deque, size_t deque_id, void* item, jh_cursor_type cursor) {
    
    if (cursor == (JH_SD_USE_HEAD(share_deque, deque_id)))
        return -1;
    
    memcpy(item, JH_SD_ELEMENT_ADDR(share_deque, cursor), share_deque->item_size);
    
    JH_SD_GET(share_deque, JH_SD_GET(share_deque, cursor, next), prev) = JH_SD_GET(share_deque, cursor, prev);
    JH_SD_GET(share_deque, JH_SD_GET(share_deque, cursor, prev), next) = JH_SD_GET(share_deque, cursor, next);
    JH_SD_GET(share_deque, cursor, next) = JH_SD_NULL;
    JH_SD_GET(share_deque, cursor, prev) = JH_SD_NULL;
    
    JH_SD_GET(share_deque, cursor, next) = JH_SD_GET(share_deque, JH_SD_IDLE_HEAD, next);
    JH_SD_GET(share_deque, JH_SD_IDLE_HEAD, next) = cursor;

    return 0;
}
/************************************************************************/
/*                                                                      */
/************************************************************************/
/**
  * @brief      判断共享双端队列是否为空
  * @param[in]  share_deque 共享双端队列
  * @param[in]  deque_id    双端队列编号
  *
  * @return     errcode
  * @retval      1          共享双端队列为空
  * @retval      0          共享双端队列不为空
  * @retval     -1          失败
  */
int jh_share_deque_is_empty(jh_share_deque_t* share_deque, size_t deque_id) {
    if(share_deque == NULL)
        return -1;
    
    return JH_SD_GET(share_deque, JH_SD_USE_HEAD(share_deque, deque_id), next) == JH_SD_USE_HEAD(share_deque, deque_id);
}

/**
  * @brief      判断共享双端队列是否已满
  * @param[in]  share_deque 共享双端队列
  *
  * @return     errcode
  * @retval      1          共享双端队列已满
  * @retval      0          共享双端队列未满
  * @retval     -1          失败
  */
int jh_share_deque_is_full(jh_share_deque_t* share_deque) {
    if(share_deque == NULL)
        return -1;
    
    return JH_SD_GET(share_deque, JH_SD_IDLE_HEAD, next) == JH_SD_NULL;
}

/**
  * @brief      获取共享双端队列的数据数量
  * @param[in]  share_deque 共享双端队列
  * @param[in]  deque_id    双端队列编号
  *
  * @return     errcode
  * @retval     >=0         共享双端队列的数据数量
  */
size_t jh_share_deque_count(jh_share_deque_t* share_deque, size_t deque_id) {
    jh_cursor_type cursor, cnt = 0;
    
    if(share_deque == NULL)
        return 0;
    
    for (cursor = JH_SD_GET(share_deque, JH_SD_USE_HEAD(share_deque, deque_id), next); cursor != JH_SD_USE_HEAD(share_deque, deque_id); cursor = JH_SD_GET(share_deque, cursor, next)) {
        cnt++;
    }
    return cnt;
}

/**
  * @brief      数据添加到队首
  * @param[in]  share_deque 共享双端队列
  * @param[in]  deque_id    双端队列编号
  * @param[in]  item        要入队的数据项
  *
  * @return     errcode
  * @retval      0          成功
  * @retval     -1          失败
  */
int jh_share_deque_push_head(jh_share_deque_t* share_deque, size_t deque_id, void* item) {
    return jh_share_deque_push(share_deque, item, JH_SD_USE_HEAD(share_deque, deque_id));
}

/**
  * @brief      数据添加到队尾
  * @param[in]  share_deque 共享双端队列
  * @param[in]  deque_id    双端队列编号
  * @param[in]  item        要入队的数据项
  *
  * @return     errcode
  * @retval      0      成功
  * @retval     -1      失败
  */
int jh_share_deque_push_tail(jh_share_deque_t* share_deque, size_t deque_id, void* item) {
    return jh_share_deque_push(share_deque, item, JH_SD_GET(share_deque, JH_SD_USE_HEAD(share_deque, deque_id), prev));
}

/**
  * @brief      队首数据出队
  * @param[in]  share_deque 共享双端队列
  * @param[in]  deque_id    双端队列编号
  * @param[in]  item        要出队的数据项
  *
  * @return     errcode
  * @retval      0          成功
  * @retval     -1          失败
  */
int jh_share_deque_pop_head(jh_share_deque_t* share_deque, size_t deque_id, void* item) {
    return jh_share_deque_pop(share_deque, deque_id, item, JH_SD_GET(share_deque, JH_SD_USE_HEAD(share_deque, deque_id), next));
}

/**
  * @brief      队尾数据出队
  * @param[in]  share_deque 共享双端队列
  * @param[in]  deque_id    双端队列编号
  * @param[in]  item        要出队的数据项
  *
  * @return     errcode
  * @retval      0          成功
  * @retval     -1          失败
  */
int jh_share_deque_pop_tail(jh_share_deque_t* share_deque, size_t deque_id, void* item) {
    return jh_share_deque_pop(share_deque, deque_id, item, JH_SD_GET(share_deque, JH_SD_USE_HEAD(share_deque, deque_id), prev));
}

/**
  * @brief      查看队首的数据
  * @param[in]  share_deque 共享双端队列
  * @param[in]  deque_id    双端队列编号
  * @param[in]  item        队首的数据项
  *
  * @return     errcode
  * @retval      0          成功
  * @retval     -1          失败
  */
int jh_share_deque_peek_head(jh_share_deque_t* share_deque, size_t deque_id, void* item) {
    if (JH_SD_GET(share_deque, JH_SD_USE_HEAD(share_deque, deque_id), next) == (JH_SD_USE_HEAD(share_deque, deque_id)))
        return -1;
    
    memcpy(item, JH_SD_ELEMENT_ADDR(share_deque, JH_SD_GET(share_deque, JH_SD_USE_HEAD(share_deque, deque_id), next)), share_deque->item_size);

    return 0;
}

/**
  * @brief      查看队尾的数据
  * @param[in]  share_deque 共享双端队列
  * @param[in]  deque_id    双端队列编号
  * @param[in]  item        队尾的数据项
  *
  * @return     errcode
  * @retval      0          成功
  * @retval     -1          失败
  */
int jh_share_deque_peek_tail(jh_share_deque_t* share_deque, size_t deque_id, void* item) {
    if (JH_SD_GET(share_deque, JH_SD_USE_HEAD(share_deque, deque_id), prev) == (JH_SD_USE_HEAD(share_deque, deque_id)))
        return -1;
    
    memcpy(item, JH_SD_ELEMENT_ADDR(share_deque, JH_SD_GET(share_deque, JH_SD_USE_HEAD(share_deque, deque_id), prev)), share_deque->item_size);

    return 0;
}

/**
  * @brief      清空共享双端队列
  * @param[in]  share_deque 共享双端队列
  * @param[in]  deque_id    双端队列编号
  *
  * @return     errcode
  * @retval      0          成功
  * @retval     -1          失败
  */
int jh_share_deque_clear(jh_share_deque_t* share_deque, size_t deque_id) {
    jh_cursor_type cursor, nextcursor;
    
    for (cursor = JH_SD_GET(share_deque, JH_SD_USE_HEAD(share_deque, deque_id), next), nextcursor = JH_SD_GET(share_deque, cursor, next); cursor != (JH_SD_USE_HEAD(share_deque, deque_id)); cursor = nextcursor, nextcursor = JH_SD_GET(share_deque, cursor, next)) {
        JH_SD_GET(share_deque, cursor, next) = JH_SD_GET(share_deque, JH_SD_IDLE_HEAD, next);
        JH_SD_GET(share_deque, JH_SD_IDLE_HEAD, next) = cursor;
    }
    
    JH_SD_GET(share_deque, JH_SD_USE_HEAD(share_deque, deque_id), next) = JH_SD_USE_HEAD(share_deque, deque_id);
    JH_SD_GET(share_deque, JH_SD_USE_HEAD(share_deque, deque_id), prev) = JH_SD_USE_HEAD(share_deque, deque_id);
    
    return 0;
}

/**
  * @brief      获取共享双端队列指定位置的指定数量数据
  * @param[in]  share_deque 共享双端队列
  * @param[in]  deque_id    双端队列编号
  * @param[in]  item        获取的数据项
  * @param[in]  start       指定的起始位置
  * @param[in]  num         指定的数量
  *
  * @return     errcode
  * @retval     >=0         成功获取的数据数量
  * @retval     -1          失败
  */
int jh_share_deque_get_data(jh_share_deque_t* share_deque, size_t deque_id, void* item, size_t start, size_t num) {
    jh_cursor_type cursor, cnt = 0, len = 0;
    for (cursor = JH_SD_GET(share_deque, JH_SD_USE_HEAD(share_deque, deque_id), next); cursor != JH_SD_USE_HEAD(share_deque, deque_id); cursor = JH_SD_GET(share_deque, cursor, next)) {
        if(cnt >= start) {
            memcpy(item + len * share_deque->item_size, JH_SD_ELEMENT_ADDR(share_deque, cursor), share_deque->item_size);
            len++;
            if(len >= num)
                break;
        }
        cnt++;
    }
    return len;
}

/**
  * @brief      共享双端队列基础初始化
  * @param[in]  share_deque 共享双端队列
  * @param[in]  deque_num   双端队列数量
  * @param[in]  base        指向用于共享双端队列的数组
  * @param[in]  num         数组中能存放的数据项数量
  * @param[in]  size        数组中每个数据项的大小
  *
  * @return     errcode
  * @retval      0          成功
  * @retval     -1          失败
  */
int jh_share_deque_base_init(jh_share_deque_t* share_deque, size_t deque_num, void* base, size_t num, size_t size) {
    if(share_deque == NULL || base == NULL)
        return -1;
    
    int i;
    
    share_deque->base = base;
    share_deque->num = num;
    share_deque->size = size / share_deque->num;
    share_deque->item_size = share_deque->size - 2 * sizeof(jh_cursor_type);
    share_deque->offset = 2 * sizeof(jh_cursor_type);
    
    for (i = 0; i < share_deque->num; i++) {
        JH_SD_GET(share_deque, i, next) = i + 1;
    }
    
    JH_SD_GET(share_deque, JH_SD_USE_HEAD(share_deque, deque_num) - 1, next) = JH_SD_NULL;
    
    for (i = 1; i <= deque_num; i++) {
        JH_SD_GET(share_deque, JH_SD_USE_HEAD(share_deque, i), next) = JH_SD_USE_HEAD(share_deque, i);
        JH_SD_GET(share_deque, JH_SD_USE_HEAD(share_deque, i), prev) = JH_SD_USE_HEAD(share_deque, i);
    }
    
    return 0;
}

/**
  * @brief      共享双端队列初始化
  * @param[in]  share_deque 共享双端队列
  * @param[in]  deque_num   双端队列数量
  * @param[in]  base        指向用于共享双端队列的数组
  * @param[in]  num         数组中能存放的数据项数量
  * @param[in]  size        数组中每个数据项的大小
  *
  * @return     errcode
  * @retval      0          成功
  * @retval     -1          失败
  */
int jh_share_deque_init(jh_share_deque_t* share_deque, size_t deque_num, void* base, size_t num, size_t size) {
    return jh_share_deque_base_init(share_deque, deque_num, base, num + deque_num + 1, size);
}

#ifdef JH_SHARE_QUEUE_DYNAMIC_SPACE
/**
  * @brief      双端队列共享空间申请
  * @param[in]  share_deque 共享双端队列
  * @param[in]  deque_num   双端队列数量
  * @param[in]  num         数组中能存放的数据项数量
  * @param[in]  size        数组中每个数据项的大小
  *
  * @return     errcode
  * @retval      0          成功
  * @retval     -1          失败
  */
int jh_share_deque_space_malloc(jh_share_deque_t* share_deque, size_t deque_num, size_t num, size_t size) {
    int i;
    
    if(share_deque == NULL)
        return -1;
    
    share_deque->num = num + deque_num + 1;
    share_deque->size = size + sizeof(jh_cursor_type) * 2;
    share_deque->item_size = share_deque->size - 2 * sizeof(jh_cursor_type);
    share_deque->offset = 2 * sizeof(jh_cursor_type);
    share_deque->base = (void *)JH_SHARE_QUEUE_MALLOC(share_deque->size * share_deque->num);
    if(share_deque->base == NULL)
        return -1;
    
    for (i = 0; i < share_deque->num; i++) {
        JH_SD_GET(share_deque, i, next) = i + 1;
    }
    
    JH_SD_GET(share_deque, JH_SD_USE_HEAD(share_deque, deque_num) - 1, next) = JH_SD_NULL;
    
    for (i = 1; i <= deque_num; i++) {
        JH_SD_GET(share_deque, JH_SD_USE_HEAD(share_deque, i), next) = JH_SD_USE_HEAD(share_deque, i);
        JH_SD_GET(share_deque, JH_SD_USE_HEAD(share_deque, i), prev) = JH_SD_USE_HEAD(share_deque, i);
    }
    
    return 0;
}

/**
  * @brief      双端队列共享空间释放
  * @param[in]  share_deque 共享双端队列
  *
  * @return     errcode
  * @retval      0          成功
  * @retval     -1          失败
  */
int jh_share_deque_space_free(jh_share_deque_t* share_deque) {
    if(share_deque == NULL || share_deque->base == NULL)
        return -1;
    JH_SHARE_QUEUE_FREE(share_deque->base);
    return 0;
}
#endif
